﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Interop;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Converters;
using System.Windows.Media.Composition;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace CPUMeterGadget
{
    public class CPUMeterGadget : GadgetBase.GadgetWindow
    {
        private string imageFolder;
        private const string imageFolderName = @"images\";
        private const string blueBarImageName = "barblue.png";
        private const string greenBarImageName = "bargreen.png";
        private const string backBarImageName = "barback.png";        

        private CPUMeter cpuMeter = new CPUMeter();
        private Image blueBar;
        private Image greenBar;
        private Label cpuLabel = new Label();
        private Label cpuValueLabel = new Label();
        private Label memoryLabel = new Label();
        private Label memoryValueLabel = new Label();

        private GadgetBase.Utility.MemoryStatus memoryStatus = new GadgetBase.Utility.MemoryStatus();

        private PerformanceCounter processorCounter;
        public DispatcherTimer processorTimer = new DispatcherTimer();
        public DispatcherTimer memoryTimer = new DispatcherTimer();
        
        Path cpuGraph = new Path();
        
        List<int> cpuHistory = new List<int>(111);

        public CPUMeterGadget()
            : base()
        {

            this.Title = "CPU Meter";

            base.Width = 130;
            base.Height = 135;
            
            for (int i = 0; i < 110; i++)
            {
                cpuHistory.Add(0);
            }

            this.processorCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            base.Children.Add(this.cpuMeter);

            this.imageFolder = GadgetBase.GlobalMethods.GetFullPath(imageFolderName);

            this.blueBar = GadgetBase.GlobalMethods.GetImage(imageFolder + blueBarImageName);             
            this.blueBar.Margin = new Thickness(7, 25, 43, 3);
            this.blueBar.HorizontalAlignment = HorizontalAlignment.Left;
            this.blueBar.VerticalAlignment = VerticalAlignment.Top;                        

            this.greenBar = GadgetBase.GlobalMethods.GetImage(imageFolder + greenBarImageName);
            this.greenBar.Margin = new Thickness(7, 35, 43, 3);
            this.greenBar.HorizontalAlignment = HorizontalAlignment.Left;
            this.greenBar.VerticalAlignment = VerticalAlignment.Top;
            

            this.cpuValueLabel.Foreground = new SolidColorBrush(Color.FromRgb(0x87, 0xCE, 0xFA));
            Thickness cpuValueLabelMargin = this.blueBar.Margin;
            cpuValueLabelMargin.Top -= 10;
            cpuValueLabelMargin.Right = 3;
            this.cpuValueLabel.Margin = cpuValueLabelMargin;
            this.cpuValueLabel.HorizontalAlignment = HorizontalAlignment.Right;
            this.cpuValueLabel.FontFamily = new FontFamily("Segoe UI");
            this.cpuValueLabel.FontSize = 9;

            this.cpuLabel.Content = "CPU";
            this.cpuLabel.Margin = new Thickness(this.cpuValueLabel.Margin.Left + this.blueBar.Source.Width, this.cpuValueLabel.Margin.Top, 0, 0);
            this.cpuLabel.Foreground = this.cpuValueLabel.Foreground;
            this.cpuLabel.FontFamily = this.cpuValueLabel.FontFamily;
            this.cpuLabel.FontSize = this.cpuValueLabel.FontSize;
            this.cpuLabel.FontWeight = this.cpuValueLabel.FontWeight;

            RefreshProcessorValues();

            
            this.memoryValueLabel.Foreground = new SolidColorBrush(Color.FromRgb(0x90, 0xEE, 0x90));
            Thickness memoryLabelMargin = this.greenBar.Margin;
            memoryLabelMargin.Top -= 10;
            memoryLabelMargin.Right = 3;
            this.memoryValueLabel.Margin = memoryLabelMargin;
            this.memoryValueLabel.HorizontalAlignment = HorizontalAlignment.Right;
            this.memoryValueLabel.FontFamily = this.cpuValueLabel.FontFamily;
            this.memoryValueLabel.FontSize = this.cpuValueLabel.FontSize;
            this.memoryValueLabel.FontWeight = this.cpuValueLabel.FontWeight;            

            this.memoryLabel.Content = "RAM";
            this.memoryLabel.Margin = new Thickness(this.memoryValueLabel.Margin.Left + this.greenBar.Source.Width, this.memoryValueLabel.Margin.Top, 0, 0);
            this.memoryLabel.Foreground = this.memoryValueLabel.Foreground;
            this.memoryLabel.FontFamily = this.memoryValueLabel.FontFamily;
            this.memoryLabel.FontSize = this.memoryValueLabel.FontSize;
            this.memoryLabel.FontWeight = this.memoryValueLabel.FontWeight;
            
            RefreshPhysicalMemory();


            double graphTop = 34;
            double graphLeft = -5;
            double graphWidth = 110;
            double graphHeight = 60;

            cpuGraph.Width = graphWidth;
            cpuGraph.Height = graphHeight;
            cpuGraph.Margin = new Thickness(graphLeft, graphTop, 0, 0);
            cpuGraph.Stretch = Stretch.Fill;
            //cpuGraph.StrokeLineJoin = PenLineJoin.Round;
            //cpuGraph.StrokeMiterLimit = 100;
            cpuGraph.Stroke = new SolidColorBrush(Color.FromRgb(0x87, 0xCE, 0xFA));
            cpuGraph.StrokeThickness = 1;            
            
            

            Path xLine = new Path();
            xLine.Width = graphWidth;
            xLine.Height = graphHeight;
            xLine.Data = PathGeometry.Parse(" M 5,100 L 5,0  M 15,100 L 15,0  M 25,100 L 25,0  M 35,100 L 35,0  M 45,100 L 45,0  M 55,100 L 55,0  M 65,100 L 65,0  M 75,100 L 75,0  M 85,100 L 85,0  M 95,100 L 95,0  M 105,100 L 105,0 ");
            xLine.Margin = new Thickness(graphLeft, graphTop, 0, 0);
            xLine.Stretch = Stretch.Fill;
            xLine.Stroke = new SolidColorBrush(Color.FromRgb(0x2F, 0x4F, 0x4F));
            xLine.StrokeThickness = 1;            

            Path yLine = new Path();
            yLine.Width = graphWidth;
            yLine.Height = graphHeight;
            yLine.Stretch = Stretch.Fill;
            yLine.Data = PathGeometry.Parse(" M 0,20 L 110,20  M 0,40 L 110,40  M 0,60 L 110,60  M 0,80 L 110,80");
            yLine.Margin = new Thickness(graphLeft, graphTop, 0, 0);
            yLine.Stroke = new SolidColorBrush(Color.FromRgb(0x2F, 0x4F, 0x4F));
            yLine.StrokeThickness = 1;

            Path borderLine = new Path();
            borderLine.Width = graphWidth;
            borderLine.Height = graphHeight;
            borderLine.Stretch = Stretch.Fill;
            borderLine.Data = PathGeometry.Parse(" M 0,100 L 110,100 L 110,0 L 0,0 L 0,100");
            borderLine.Margin = new Thickness(graphLeft, graphTop, 0, 0);
            borderLine.Stroke = new SolidColorBrush(Color.FromRgb(0x77, 0x88, 0x99));
            borderLine.StrokeThickness = 1;
            

            base.Children.Add(GetBackBar(blueBar));
            base.Children.Add(blueBar);
            base.Children.Add(cpuLabel);            
            base.Children.Add(cpuValueLabel);
            base.Children.Add(GetBackBar(greenBar));
            base.Children.Add(greenBar);
            base.Children.Add(memoryLabel);
            base.Children.Add(memoryValueLabel);            
            base.Children.Add(xLine);
            base.Children.Add(yLine);
            base.Children.Add(cpuGraph);
            base.Children.Add(borderLine);            

            this.processorTimer.Interval = new TimeSpan(0, 0, 0, 0, 700);
            this.processorTimer.Tick += new EventHandler(this.processorTimer_Tick);
            this.processorTimer.IsEnabled = true;

            this.memoryTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
            this.memoryTimer.Tick += new EventHandler(this.memoryTimer_Tick);
            this.memoryTimer.IsEnabled = true;
        }

        protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            if (e.ChangedButton == MouseButton.Left)
            {
                System.Diagnostics.Process.Start("taskmgr.exe");
            }
        }


        public class CPUMeter : GadgetBase.Gadgets.Gadget
        {
            private string folder;
            private const string backgroundImageName = "black_back_b.png";                        

            public CPUMeter()
            {
                this.folder = GadgetBase.GlobalMethods.GetFullPath(imageFolderName);

                SetBackground(folder + backgroundImageName);
            }
        }


        private void SetBarValue(Image bar, int value)
        {
            double width = bar.Source.Width * value / 100;

            bar.Width = width;
        }


        private Image GetBackBar(Image bar)
        {            
            Image backBar = GadgetBase.GlobalMethods.GetImage(imageFolder + backBarImageName);
            backBar.Margin = bar.Margin;
            backBar.HorizontalAlignment = bar.HorizontalAlignment;
            backBar.VerticalAlignment = bar.VerticalAlignment;

            return backBar;
        }


        private void RefreshProcessorValues()
        {
            int value = (int)this.processorCounter.NextValue();
            PushCPUValue(value);
            SetBarValue(this.blueBar, value);
            this.cpuValueLabel.Content = value + "%";            
        }

        private void PushCPUValue(int value)
        {
            cpuHistory.RemoveAt(0);
            cpuHistory.Add(value);
        }

        private void RefreshCPUGraph()
        {
            string c1path = " M 0,100 ";
            int c1 = 0;
            string param = "";

            int i = 0;
            int oldValue = 0;
            foreach(int v in cpuHistory)
            {
                c1 = v;
                param = (c1 == 0) ? "M" : "L";
                c1path += string.Format(" M {0},{4} {1} {2},{3}", i + 1, param, i + 1, 100 - c1, 100 - oldValue);
                oldValue = v;
                i++;
            }
          
            this.cpuGraph.Data = PathGeometry.Parse(c1path);
        }

        private void RefreshPhysicalMemory()
        {
            memoryStatus.Update();
            SetBarValue(greenBar, (int)memoryStatus.MemoryLoad);            
            this.memoryValueLabel.Content = ((memoryStatus.TotalPhysical - memoryStatus.AvailablePhysical) / 1024 / 1024) + "Мб";
        }

        private void processorTimer_Tick(object sender, EventArgs e)
        {            
            RefreshProcessorValues();
        }

        private void memoryTimer_Tick(object sender, EventArgs e)
        {
            RefreshCPUGraph();
            RefreshPhysicalMemory();            
        }
    }
}
